package org.mmtk.plan.scale.balance;

import org.mmtk.plan.scale.Scale;
import org.mmtk.plan.scale.ScaleTrace;
import org.mmtk.plan.scale.ScaleTraceLocal;
import org.mmtk.plan.scale.metrix.ClosureCounts;
import org.mmtk.plan.scale.utils.ScaleLogger;
import org.mmtk.policy.Space;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.ObjectReference;

@Uninterruptible
public final class SBalanceTraceLocal extends ScaleTraceLocal {
	private ClosureCounts fixClosureCounts;	
	public SBalanceTraceLocal(String name, ScaleTrace trace) {
		this(name, SBalance.SCAN_BALANCE, trace);
	}
	
	public SBalanceTraceLocal(String name, int scan, ScaleTrace trace) {
		super(name, scan, trace);
		this.fixClosureCounts = new ClosureCounts("fixClosureCounts");
	}
	
	@Override
	protected void doSetClosureCounts() {		
		if(SBalance.getTraceFlavor() == SBalance.METRIX_FIX_METRIX_FLAVOR) {
			if(SBalance.closure == SBalance.CLOSURE_FIRST_SINGLE ) {	
				this.initialTrace = true;
				currentClosureCounts = this.depthClosureCounts;	
			} else if (SBalance.closure == SBalance.CLOSURE_SINGLE) {
				this.initialTrace = false;
				currentClosureCounts = this.fixClosureCounts;	
			} else if(SBalance.closure == SBalance.CLOSURE_LAST_SINGLE) {
				this.initialTrace = false;
				currentClosureCounts = this.checkClosureCounts;	
			} else if (VM.VERIFY_ASSERTIONS) {
				VM.assertions._assert(false,
				"SBalanceTraceLocal.doSetClosureCounts encountered wrong closure");
			}
		} else if (VM.VERIFY_ASSERTIONS) {
			VM.assertions._assert(false,
					"SBalanceTraceLocal.doSetClosureCounts encountered wrong flavor");
		}		
	}

	void completeFixTrace(TraceBranchBalancer ballancer, boolean save) {
		if(Options.scaleDebug.getValue() > 3) {
			ScaleLogger.acquireLogLock();
			
			Log.prependThreadId();
			Log.write("SBalanceTraceLocal.completeFixTrace - enter, name=");
			Log.writeln(this.getName());
			Log.flush();
			
			ScaleLogger.releaseLogLock();
		}
		boolean stopBalance = false;
		ballancer.initBalance();
		
	    if (!rootLocations.isEmpty() && Options.scaleDebug.getValue() > 3) {
	    	ScaleLogger.logConcMessage("completeFixTrace - roots not empty, processing roots");
	    	processRoots(save);
	    }
	    
	    assertMutatorRemsetsFlushed();
	    
	    ObjectReference v;
	    boolean inMySpace;
	    long level;
	    int children;
	    do {
	      while (!values.isEmpty()) {
	        v = values.pop();
	        currentClosureCounts.addPopped(v);
	        inMySpace = Space.isInSpace(Scale.SCALE_MS_SPACE, v);
	        if(inMySpace) {
	        	currentClosureCounts.addMsPopped();
	        	level = currentClosureCounts.getMsPushed() - currentClosureCounts.getMsPopped();   	
	        	children = scanObjectPlus(v);  
	        	if(level < 100) {
	        		stopBalance = true;
	        	} if(!stopBalance) {
	        		ballancer.processObject(v, level, children);	
	        	}
	        } else {
	        	children = scanObjectPlus(v);
	        	if(children > 0 && Options.scaleDebug.getValue() > 3) { 
	        		Log.write("Non ms object created ms children ");Log.writeln(children);
	        		Log.flush();
	        	}
	        }
	      }
	      processRememberedSets();
	    } while (!values.isEmpty());
	    assertMutatorRemsetsFlushed();
	    
	    if(Options.scaleDebug.getValue() > 3) {
	    	currentClosureCounts.endClosure();
	    	Log.writeln("completeFixTrace - done");
	    	ballancer.reportBalance();
	    	Log.flush();
	    }
	}
	
	private int scanObjectPlus(ObjectReference v) {
		long pushedBefore = currentClosureCounts.getMsPushed();	
		scanObject(v);
		long pushedAfter = currentClosureCounts.getMsPushed();
		return (int)(pushedAfter - pushedBefore);
	}
}
